import torch
import numpy as np
x = torch.empty(5, 3)
# 创建一个5*3的未初始化的tensor
print(x)

y = torch.rand(5, 3)
# 创建一个5*3的随机初始化的tensor
print(y)

z = torch.zeros((5, 3), dtype=torch.long)
# 创建一个5*3的long型全为0的tensor
print(z)

p = torch.tensor([5.5, 3])
# 直接根据数据创建
print(p)

x = x.new_ones(5, 3, dtype=torch.float64)
#  返回的tensor默认具有相同的torch.dtype和torch.device
print(x)

x = torch.randn_like(x, dtype=float)
#  指定新的数据类型
print(x)

print(x.shape)
print(x.size())
# 通过shape和size来获取tensor的形状
#  注意：返回的torch.Size其实就是⼀个tuple, ⽀持所有tuple的操作

# 算术操作 加法
print(x+y)
# 加法形式一

print(torch.add(x, y))
# 加法形式二

result = torch.empty((5, 3))
torch.add(x, y, out=result)
print(result)
# 指定输出


# 广播机制
x = torch.arange(1, 3).view(1, 2)
print("x:", x)
y = torch.arange(1, 4).view(3, 1)
print("y:", y)
print("x+y:", x+y)

# 内存开销， 索引，view不会开辟新内存，而类似y=x+y会开辟新内存，将y指向新内存，通过id（）函数来查看内存地址是否改变
print("y_id:", id(y))
before = id(y)
y = x+y
print("y_id_after:", id(y))
print(id(y)==before)

# tensor与numpy的互换
# 我们很容易易⽤numpy()和 from_numpy()将tensor与Numpy数组互换，
# 但是需要注意的一点是这两个函数所产⽣生的tensor和NumPy中的数组共享相同的内存（所以他们之间的转换很
# 快），改变其中⼀一个时另⼀一个也会改变！！！

# 还有⼀一个常⽤用的将NumPy中的array转换成tensor的方法就是torch.tensor(),
#  需要注意的的此⽅方法总是会进行数据拷贝（就会消耗更更多的时间和空间），所以返回的tensor和原来的数据不不再共享内存

# tensor转Numpy,使用numpy()将tensor转换成numpy数组, numpy()共享内存
a = torch.ones(5)
b = a.numpy()
print(a, b)

a += 1
print(a, b)

b += 1
print(a, b)

# Numpy数组转tensor, 使用torch.from_numpy(), from_numpy()共享内存
a = np.ones(5)
b = torch.from_numpy(a)
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)

# 使用torch.tensor()将numpy 转换为tensor,该方法会进行数据拷贝，不共享内存
a = np.ones(5)
b = torch.tensor(a)
print(a, b)
a += 1
print(a, b)
b += 1
print(a, b)

# 方法to()可以将tensor在cpu和gpu上互相移动
if torch.cuda.is_available():
    device = torch.device("cuda")
    y = torch.ones_like(x, device=device)
#tensor
    x = x.to(device)
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))